/**
	Copyright (c) 2009 Ozazo Pvt Ltd, INDIA

	Permission is hereby granted, free of charge, to any person or organization 
	obtaining a copy of this software and associated  documentation  files (the 
	"Software"), to deal in the Software without restriction, including without 
	limitation the rights to use, copy, modify, merge, publish, distribute, sub
	license, and/or sell copies of the Software, and to permit persons  to whom 
	the Software is furnished to do so, subject to the following conditions:
	
	The above copyright notice and  this permission notice shall be included in 
	all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS",  WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING  BUT NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY,  WHETHER  IN  AN  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
	FROM,  OUT  OF  OR  IN  CONNECTION  WITH  THE  SOFTWARE OR THE USE OR OTHER 
	DEALINGS IN THE SOFTWARE.
 */
package com.ozazo.wave.robots.fw.engines;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import com.ozazo.wave.robots.engines.BlipDataParser;
import com.ozazo.wave.robots.engines.LateXEngine;
import com.ozazo.wave.robots.engines.WikiEngine;
import com.ozazo.wave.robots.fw.config.ContainerConfig;
import com.ozazo.wave.robots.fw.config.EngineConfig;
import com.ozazo.wave.robots.fw.config.RobotConfig;
import com.ozazo.wave.robots.fw.utils.RobotConfigSAXParser;


/**
 * @author:  Araf Karsh Hamid
 * @date:
 * @serial:
 * @version: 1.0
 */
public final class RobotContainerFactory {

	private static final RobotContainerFactory factory = new RobotContainerFactory();

	private final Logger log	= Logger.getLogger("RobotContainer");
	private final String configFile;
	
	private BlipDataParserInterface blipDataParser;	
	private RobotConfig robotConfig;
	
	private RobotContainerFactory() {
		configFile = System.getProperty("botConfig");
		log.severe("RobotContainer Factor loading config file = "+configFile);
		robotConfig = loadConfig();
		loadParser(robotConfig);
	}
	
	private RobotConfig loadConfig() {
		RobotConfig rConfig = null;
		// configFile = System.getProperty("botConfig");
		// log.severe("RobotContainer Factor loading config file = "+configFile);
		if(configFile != null) {
			RobotConfigSAXParser parser = new RobotConfigSAXParser();
			try {
				rConfig = parser.parse(new BufferedReader(new FileReader(configFile)));
			} catch (FileNotFoundException e) {
				log.severe("Configuration File Not Found: "+configFile);
				e.printStackTrace();
			} catch (Exception e) {
				log.severe("Configuration File Parsing issues: "+configFile);
				e.printStackTrace();
			}
		}
		log.severe(rConfig.toXMLString());
		return rConfig;
	}

	private void loadParser(final RobotConfig _config) {
		if(_config == null) {
			// Load the default Engines.
			blipDataParser = new BlipDataParser(log);
			log.severe("RobotContainerFactory> Null Config file - loading default blip data parser!");
		} else {
			// Load the Parser as per the configuration file
			ContainerConfig container = _config.getContainers().get(0);
			try {
				blipDataParser = (BlipDataParserInterface) Class.forName(container.getParserConfig().getClassName()).newInstance();
			} catch (InstantiationException e) {
				log.severe("ParserConfig = "+container.getParserConfig().getClassName()+" Error : "+e.getMessage());
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				log.severe("ParserConfig = "+container.getParserConfig().getClassName()+" Error : "+e.getMessage());
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				log.severe("ParserConfig = "+container.getParserConfig().getClassName()+" Error : "+e.getMessage());
				e.printStackTrace();
			}
			if(blipDataParser == null) {
				log.severe("RobotContainerFactory> Unable to load blip data parser! Container DISABLED.");
			}
		}	
	}
	
	private List<RobotEngineInterface> loadEngines(final RobotConfig _config) {
		List<RobotEngineInterface> engines = new ArrayList<RobotEngineInterface>(3);
		if(_config == null) {
			// Load the default Engines.
			engines.add(new WikiEngine(log));
			engines.add(new LateXEngine(log));
		} else {
			// Load the engines as per the configuration file
			// Load the first Container.
			ContainerConfig container = _config.getContainers().get(0);
			for(EngineConfig engineConfig : container.getEngines()) {
				log.severe("Engine Load Flag for "+ engineConfig.getName()+" = "+engineConfig.isEnabled());
				if(engineConfig.isEnabled()) {
					RobotEngineInterface engine = null;
					try {
						engine = (RobotEngineInterface) Class.forName(engineConfig.getClassName()).newInstance();
					} catch (InstantiationException e) {
						log.severe("EngineConfig = "+engineConfig.getClassName()+" Error : "+e.getMessage());
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						log.severe("EngineConfig = "+engineConfig.getClassName()+" Error : "+e.getMessage());
						e.printStackTrace();
					} catch (ClassNotFoundException e) {
						log.severe("EngineConfig = "+engineConfig.getClassName()+" Error : "+e.getMessage());
						e.printStackTrace();
					}
					if(engine != null) {
						engines.add(engine);
					}
				}
			}
		}	
		return engines;
	}
	
	/**
	 * Get the Singleton Robot Factory Instance.
	 * 
	 * @return RobotContainerFactory
	 */
	
	public static RobotContainerFactory getInstance() {
		return factory;
	}

	/**
	 * Reloads the Configuration files and resets the Parser and the Engines.
	 */
	
	public void reloadConfig() {
		robotConfig = loadConfig();
		loadParser(robotConfig);
	}		
	
	/**
	 * Creates RobotContainer with the Singleton instance of the parser and new instances of the Engines.
	 * 
	 * @return RobotContainerInterface
	 */
	
	public RobotContainerInterface getRobotContainer() {
		RobotContainer container = new RobotContainer(log);
		container.setParsingEngine(blipDataParser);
		container.setBotEngines(loadEngines(robotConfig));
		return container;	
	}
	
	// Following blipText fields are used for command line unit testing ONLY.
	public static String blipText1 = "/wd Kerala is known as Gods own country. So is /W India. -w \"The East India Company\" South /W \"Pazhassi Raja\" the /wx warrior King.";
	public static String blipText2 = "/w Kerala aka GOC. /wd India democratic /ltx '\\sum_{n=09}^{+\\infty}\\frac{9}{s^2}=\\frac{\\pi^2}{23}' is good";
	public static String blipText3 = "/w Einstein one of the greatest minds of human species but disagreed with /w \"Quantum Physics\" saying /w \"spooky action\" at distance aka /w 'EPR Paradox' /w 'EPR again'";
	public static String blipText4 = "/w Einstein one of the greatest minds of human species but disagreed with /w \"Quantum Physics\" saying spooky action at distance. /wd Advaita Vendata was born in Kerala.";	
	public static String blipText5 = "/w Einstein greatest /w mind disagreed with /w \"Quantum Physics\" saying /w \"spooky action\" at distance aka /w 'EPR Paradox'.";
	
	/**
	 * For unit testing. 
	 * All the commands needs to be parsed with values in single, double and without quotes.
	 */

	public static void main(String args[]) throws Exception {

		RobotContainer bot = (RobotContainer) RobotContainerFactory.getInstance().getRobotContainer();
		
		// Standard testing for BlipDataParser and Engines (Wiki and LaTeX)
		
		// System.out.println("\n"+bot.processText(bot.blipText1));
		// System.out.println("\n"+bot.processText(bot.blipText2));
		// System.out.println("\n"+bot.processText(bot.blipText3));
		// System.out.println("\n"+bot.processText(bot.blipText4));

		// Following does the Unit testing for Patterns to work in Real Time and Non Real Time mode
		// Real Time = DOCUMENT_CHANGED Event
		// Non Real Time = BLIP SUBMITTED
		System.out.println("\nOUTPUT : "+bot.processText(RobotContainerFactory.blipText5));
		bot.setRealTime(true);
		System.out.println("\nOUTPUT : "+bot.processText(RobotContainerFactory.blipText5));

		bot.setRealTime(false);
		System.out.println("\nOUTPUT : "+bot.processText(RobotContainerFactory.blipText2));
		bot.setRealTime(true);
		System.out.println("\nOUTPUT : "+bot.processText(RobotContainerFactory.blipText2));

	}		
}
